﻿#region Using directives
using System;
using System.Collections;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
#endregion

namespace RainEngine.SceneManagement
{
	/// <summary>
	/// 绘制3D空间中直线的辅助类。
	/// 这个类保存一个线段集合，如果任意一条线发生变化或线的数量发生变化则重建一个新的顶点缓冲，
	/// finally will
	/// render all lines in the vertex buffer at the end of the frame (so this
	/// class is obviously only for 2D lines directly on screen, no z buffer
	/// and no stuff will be in front of the lines, because everything is
	/// rendered at the end of the frame).
	/// </summary>
	public  class LineManager3D
	{
		#region Line结构
		
        /// <summary>
		/// 用于线段的结构体，这个结构的实例会被添加到lines集合
		/// </summary>
		struct Line
		{
			// 线段起点和终点
			public Vector3 startPoint, endPoint;
			// 线段颜色
			public Color startColor, endColor;

			/// <summary>
			/// 构造函数
			/// </summary>
			public Line(Vector3 setStartPoint, Color setStartColor,Vector3 setEndPoint, Color setEndColor)
			{
				startPoint = setStartPoint;
				startColor = setStartColor;
				endPoint = setEndPoint;
				endColor = setEndColor;
			}

			/// <summary>
			/// 判断两条线段是否相等
			/// </summary>
			public static bool operator ==(Line a, Line b)
			{
				return
					a.startPoint == b.startPoint &&
					a.endPoint == b.endPoint &&
					a.startColor == b.startColor &&
					a.endColor == b.endColor;
			} // ==(a, b)

			/// <summary>
			/// 判断两条线段是否不等
			/// </summary>
			public static bool operator !=(Line a, Line b)
			{
				return
					a.startPoint != b.startPoint ||
					a.endPoint != b.endPoint ||
					a.startColor != b.startColor ||
					a.endColor != b.endColor;
			} // !=(a, b)

			/// <summary>
			/// Support Equals(.) to keep the compiler happy
			/// (because we used == and !=)
			/// </summary>
			public override bool Equals(object a)
			{
				if (a.GetType() == typeof(Line))
					return (Line)a == this;
				else
					return false; // Object is not a Line
			}

			/// <summary>
			/// Support GetHashCode() to keep the compiler happy
			/// (because we used == and !=)
			/// </summary>
			public override int GetHashCode()
			{
				return 0; // Not supported or nessescary
			} // GetHashCode()
		} // struct Line
		#endregion

		#region 字段

        private RainGE engine;
		
        /// <summary>
		/// 线段数量，默认为0
		/// </summary>
		private int numOfLines = 0;

        private Effect lineEffect;        

		/// <summary>
		/// 线段集合，it will NOT be reseted each
		/// frame like numOfLines! We will remember the last lines and
		/// only change this list when anything changes (new line, old
		/// line missing, changing line data).
		/// When this happens buildVertexBuffer will be set to true.
		/// </summary>
		private List<Line> lines = new List<Line>();

		/// <summary>
		/// Build vertex buffer this frame because the line list was changed?
		/// </summary>
		private bool buildVertexBuffer = false;

		/// <summary>
		/// 顶点缓冲
		/// </summary>
		VertexPositionColor[] lineVertices = new VertexPositionColor[MaxNumOfLines*2];

		/// <summary>
		/// 当前使用的图元数量
		/// </summary>
		private int numOfPrimitives = 0;

		/// <summary>
		/// Max. number of lines allowed to prevent to big buffer, will never
		/// be reached, but in case something goes wrong or numOfLines is not
		/// reseted each frame, we won't add unlimited lines (all new lines
		/// will be ignored if this max. number is reached).
		/// </summary>
		protected const int MaxNumOfLines = 4096;//40096;//512;//256; // more than in 2D

		/// <summary>
		/// 顶点声明
		/// </summary>
        VertexDeclaration vxDecl = null;
		#endregion

		#region 初始化
		/// <summary>
		/// 初始化LineManager3D
		/// </summary>
		public LineManager3D(RainGE engine,Scene scene)
		{
            this.engine = engine;
            if (engine.GraphicsDevice  == null)
				throw new NullReferenceException(
					"图形设备还没有初始化，无法初始化LineManager3D");

            //if (vxDecl == null)
                //vxDecl = new VertexDeclaration(engine.GraphicsDevice, VertexPositionColor.VertexElements);
            lineEffect = RainGE.ResourceContent.Load<Effect>("LineRendering");
            lineEffect.Parameters["gWorld"].SetValue(Matrix.Identity);
		}
		#endregion		
		
		public void Dispose()
		{
            if (vxDecl != null)
                vxDecl.Dispose();
            vxDecl = null;
		}

		#region Reset, AddLine and Rendering
		/// <summary>
		/// Add line
		/// </summary>
		public void AddLine(Vector3 startPoint, Color startColor,Vector3 endPoint, Color endColor)
		{
			// Don't add new lines if limit is reached
			if (numOfLines >= MaxNumOfLines)
			{
				return;
			}

			// Build line
			Line line = new Line(startPoint, startColor, endPoint, endColor);

			// Check if this exact line exists at the current lines position.
			if (lines.Count > numOfLines)
			{
				if ((Line)lines[numOfLines] != line)
				{
					// overwrite old line, otherwise just increase numOfLines
					lines[numOfLines] = line;
					// Remember to build vertex buffer in Render()
					buildVertexBuffer = true;
				}
			}
			else
			{
				// Then just add new line
				lines.Add(line);
				// Remember to build vertex buffer in Render()
				buildVertexBuffer = true;
			} // else

			// nextUpValue line
			numOfLines++;
		} // AddLine(startPoint, startColor, endPoint)

        public void clear()
        {
            lines.Clear();
            numOfLines = 0;
        }

		protected void UpdateVertexBuffer()
		{
			// 如果没有线段或数据有误则返回
			if (numOfLines == 0 || 	lines.Count < numOfLines)
			{
				numOfPrimitives = 0;
				return;
			}

			// Set all lines
			for (int lineNum = 0; lineNum < numOfLines; lineNum++)
			{
				Line line = (Line)lines[lineNum];
				lineVertices[lineNum * 2 + 0] = new VertexPositionColor(line.startPoint, line.startColor);
				lineVertices[lineNum * 2 + 1] = new VertexPositionColor(line.endPoint, line.endColor);
			} // for (lineNum)
			numOfPrimitives = numOfLines;

			// Vertex buffer was build
			buildVertexBuffer = false;
		} // UpdateVertexBuffer()

		/// <summary>
		/// 绘制本帧中的所有线段
		/// </summary>
		public void Draw()
		{
			// 是否需要重建顶点缓冲?
			if (buildVertexBuffer || numOfPrimitives != numOfLines)
			{
				UpdateVertexBuffer();
			}

			// Render lines if we got any lines to render
			if (numOfPrimitives > 0)
			{
                GraphicsDevice gd = engine.GraphicsDevice;

                //gd.VertexDeclaration = vxDecl;
                //gd.RenderState.DepthBufferEnable = true;
                //gd.RenderState.DepthBias = -0.00001f;
                //gd.RenderState.CullMode = CullMode.None;               
                
                //---------------------------------------
                //  开始绘制
                //---------------------------------------
                EffectPass pass = lineEffect.CurrentTechnique.Passes[0];
                pass.Apply ();
                gd.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, lineVertices, 0, numOfPrimitives);                
				
			}
		}
		#endregion

		
	}
}
